package com.hazmatt.android.controller;

import java.io.*;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketTimeoutException;

import com.hazmatt.android.controller.listener.AccelerometerListener;
import com.hazmatt.android.controller.manager.AccelerometerManager;

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.SeekBar;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;

/**
 * A Controller that drives the 3pi serial slave program
 * 
 * @author Matt Odille <matt.odille@gmail.com>
 * 
 */
public class ControllerActivity extends Activity implements AccelerometerListener {

	private final int socketTimeout = 10000; // in milliseconds

	private static Context CONTEXT;

	private OutputStream out;
	//private PrintWriter out;
	private InputStream in;
	private Socket s;
	private boolean isConnected = false;

	private float slider = 32.0F;

	/**************************************************************************
	 * Activity overrides
	 *************************************************************************/
	@Override
	public void onCreate( Bundle savedInstanceState )
	{
		super.onCreate( savedInstanceState );
		setContentView( R.layout.main );
		CONTEXT = this;

		
		/*
		// setup command spinner
		Spinner spinner = (Spinner) findViewById(R.id.commandSpinner);
		ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(
				this, R.array.commands_array, android.R.layout.simple_spinner_item);
		adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
		spinner.setAdapter(adapter);
		*/

		// set seekbar
		SeekBar s = (SeekBar) findViewById(R.id.slider);
		s.setOnSeekBarChangeListener( new SeekBar.OnSeekBarChangeListener() {
			public void onStartTrackingTouch(SeekBar seekBar) {
				// TODO Auto-generated method stub
				
			}

			public void onStopTrackingTouch(SeekBar seekBar) {
				// TODO Auto-generated method stub
				
			}

			public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
				slider = progress;
				((TextView) findViewById( R.id.sliderText )).setText( String.valueOf( slider ) );
			}
		});
	}

	@Override
	protected void onResume()
	{
		super.onResume();
		if ( AccelerometerManager.isSupported() ) {
			AccelerometerManager.startListening( this );
		}
	}

	@Override
	protected void onDestroy()
	{
		disconnect();
		super.onDestroy();
		if ( AccelerometerManager.isListening() ) {
			AccelerometerManager.stopListening();
		}
	}

	@Override
	public boolean onCreateOptionsMenu( Menu menu )
	{
		MenuInflater inflater = getMenuInflater();
		inflater.inflate( R.menu.menu, menu );
		return true;
	}

	@Override
	public boolean onOptionsItemSelected( MenuItem item )
	{
		switch ( item.getItemId() ) {
		case R.id.exit:
			disconnect();
			this.finish();
			break;
		}
		return true;
	}

	/**************************************************************************
	 * AccelerometerListener call-backs
	 *************************************************************************/
	public void onAccelerationChanged( float x, float y, float z, boolean tx )
	{
		//((TextView) findViewById( R.id.x )).setText( String.valueOf( x ) );
		//((TextView) findViewById( R.id.y )).setText( String.valueOf( y ) );
		//((TextView) findViewById( R.id.z )).setText( String.valueOf( z ) );
/*
		if ( tx && isConnected ) {
			// TODO: change to [12,127]
			// for the speed, take x and convert float [-9.9,9.9] to int [-127,127]
			int speed = (int)( x * ( 127.0F / 9.9F ) );

			// for the offset, take y and convert float [-9.9,9.9] to int [-9,9]
			int offset = (int)( y * ( slider / 9.9F ) );

			// limit the speed +/- offset to [-127,127]
			int m1speed = (speed + offset < -127 ? -127 : ( speed + offset > Byte.MAX_VALUE ? Byte.MAX_VALUE : speed + offset));
			int m2speed = (speed - offset < -127 ? -127 : ( speed - offset > Byte.MAX_VALUE ? Byte.MAX_VALUE : speed - offset));

			//toast( m1speed + "," + m2speed, 1000 );

			byte[] m1m2data = new byte[2];
			m1m2data[0] = (byte)m1speed;
			m1m2data[1] = (byte)m2speed;

			sendCommand( PololuCommand.M1_M2_FORWARD, m1m2data );
		}
	*/
	}

	public void onShake( float force )
	{
		// TODO Auto-generated method stub
	}


	/**************************************************************************
	 * Button handlers 
	 *************************************************************************/
	public void connectButtonHandler( View view )
	{
		if ( isConnected ) return;

		String response = "";
		try {
			// establish socket connection
			String address = ( (EditText) findViewById( R.id.address ) ).getText().toString();
			String port = ( (EditText) findViewById( R.id.port ) ).getText().toString();
			Socket s = new Socket();
			SocketAddress socketAddress = new InetSocketAddress( address, Integer.parseInt( port ) );
			s.connect( socketAddress, socketTimeout );

			// setup input/output streams
			//out = new PrintWriter( s.getOutputStream(), true );
			out = s.getOutputStream();
			in = s.getInputStream();

			isConnected = true;

			//response = in.readLine();
			//toast( "rx: " + response, 1000 );

			toast( "connected successfully!", 1000 );

		} catch ( SocketTimeoutException ste ) {
			toast( "Connection failed: Socket Timeout" + response, 2000 );
			ste.printStackTrace();
		} catch ( IOException ioe ) {
			toast( "Connection failed: IOException" + response, 2000 );
			ioe.printStackTrace();
		}
	}

	public void disconnectButtonHandler( View view )
	{
		disconnect();
	}

	public void disconnect()
	{
		if ( s == null ) return;
		if ( ! isConnected ) return;
		toast( "disconnecting...", 1000 );

		// stop motors
		sendCommand( PololuCommand.M1_FORWARD, new byte[]{(byte)0} );
		sendCommand( PololuCommand.M2_FORWARD, new byte[]{(byte)0} );

		try {
			// close i/o streams and socket
			isConnected = false;
			out.close();
			in.close();
			s.close();
			toast( "Disconnected successfully", 1000 );
		} catch ( Exception e ) {
			toast( "Disconnect failed", 2000 );
			e.printStackTrace();
		}
	}
/*
	public void sendButtonHandler( View view )
	{
		toast("sendButtonHandler", 1000);
		if ( s == null ) return;
		if ( ! isConnected ) return;

		int commandInt = Integer.parseInt(((Spinner) findViewById( R.id.commandSpinner )).getSelectedItem().toString(), 16);
		byte commandByte = (byte)commandInt;
		PololuCommand command = PololuCommand.findByCommandByte( commandByte );
		if ( command != null ) {
			String response = sendCommand( command , null );
		}
		else {
			toast("error: command == null", 1000 );
		}
	}
	*/

	public void readSensorButtonHandler( View view )
	{
		// send command
		String response = this.sendCommand(PololuCommand.IR_VALUES, null);
		((TextView) findViewById( R.id.serialText )).setText( response );
	}

	/**************************************************************************
	 * Pololu commands
	 *************************************************************************/
	public String sendCommand( PololuCommand command, byte[] dataBytes )
	{
		//toast("sending: " + command.toString(), 1000);
		String response = null;
		
		// build command buffer
		int numDataBytes = dataBytes == null ? 0 : dataBytes.length;
		byte[] buffer = new byte[1+numDataBytes];
		buffer[0] = command.getCommandByte();
		for ( int i=0; i<numDataBytes; i++ ) {
			buffer[i+1] = dataBytes[i];
		}

		// send command buffer
		try {
			out.write( buffer );
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		// get the response bytes, if any
		if ( command.getResponseBytes() > 0 ) {
			byte[] responseBuffer = new byte[command.getResponseBytes()];
			try {
				in.read(responseBuffer);
				response = new String(responseBuffer);
				//toast( "rx: " + response, 1000 );
			} catch (IOException e) {
				toast( "Error while trying to get response", 1000 );
				e.printStackTrace();
			}
		}
		return response;
	}


	/**************************************************************************
	 * Setters/getters
	 *************************************************************************/
	public static Context getContext()
	{
		return CONTEXT;
	}

	/**************************************************************************
	 * Namespace simplifiers
	 *************************************************************************/
	private void toast( String s, int duration )
	{
		Toast.makeText( this, s, duration ).show();
	}
}
